#include "config.h"

#include <dirent.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdarg.h>
#include <time.h>

#define DBG_SUBSYS S_LIBSTORAGE

#include "sysy_lib.h"
#include "cache.h"
#include "get_version.h"
#include "cluster.h"
#include "volume.h"
#include "lichstor.h"
#include "stor_root.h"
#include "lich_md.h"
#include "md_map.h"
#include "configure.h"
#include "job_dock.h"
#include "chunk.h"
#include "block.h"
#include "lichstor.h"
#include "ynet_rpc.h"
#include "net_global.h"
#include "../../storage/controller/stor_ctl.h"
#include "../../storage/storage/stor_rpc.h"
#include "ylog.h"
#include "dbg.h"
#include "system.h"

int block_root(const char *pool, fileid_t *rootid)
{
        int ret;

        ret = stor_root(pool, ISCSI_ROOT, rootid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return _errno(ret);
}

int block_init()
{
        return 0;
}

int block_xattr_get(const nid_t *_nid, const chkid_t *chkid, const char *key,
                   char *value, int *valuelen)
{
        return _errno(md_xattr_get(_nid, chkid, key, value, valuelen));
}

int block_xattr_set(const nid_t *_nid, const chkid_t *chkid, const char *key,
                   char *value, int valuelen)
{
        return _errno(md_xattr_set(_nid, chkid, key, value, valuelen, 0));
}

int block_xattr_get_extend(const nid_t *_nid, const chkid_t *chkid, const char *key,
                char *name, char *password)
{
        int ret, i, buflen = MAX_MSG_SIZE, count = 0, column, row, j;
        char buf[MAX_MSG_SIZE] = {};
        char _key[MAX_MSG_SIZE] = {};
        char *items[10];
        int icount = 10;
        char *auth[2];
        int acount = 2;
        const nid_t *nid;
        nid_t tmp;

        if (strcmp(key, LICH_SYSTEM_ATTR_AUTH) == 0) {
                strcpy(_key, LICH_SYSTEM_ATTR_USERCOUNT);
        }

        if (_nid == NULL) {
                ret = md_map_getsrv(chkid, &tmp);
                if (unlikely(ret))
                        GOTO(err_ret, ret);

                nid = &tmp;
        } else
                nid = _nid;

        ret = md_xattr_get(nid, chkid, _key, buf, &buflen);
        if (unlikely(ret))
                GOTO(err_ret, ret);
        else {
                YASSERT(IQN_MAX_LEN < 256);
                count = atoi(buf);
                column = 256 / IQN_MAX_LEN;
                row = count % column ? (count / column + 1) : (count / column);
                for (i = 0; i < row; i++) {
                        buflen = MAX_MSG_SIZE;
                        sprintf(_key, "%s%d", key, i);
                        ret = md_xattr_get(nid, chkid, _key, buf, &buflen);
                        if (unlikely(ret)) {
                                if (ret != ENOKEY)
                                        GOTO(err_ret, ret);
                        }
                        _str_split(buf, ',', items, &icount);
                        for (j = 0; j < icount; j++) {
                                _str_split(items[j], ':', auth, &acount);
                                if (acount == 2 && strcmp(auth[0], name) == 0) {
                                        strcpy(password, auth[1]);
                                        return 0;
                                }
                        }
                }
        }

        return ENOENT;
err_ret:
        return _errno(ret);
}

int block_reset(const char *pool, const chkid_t *id)
{
        return _errno(stor_reset(pool, id));
}

int block_getattr(const char *pool, const chkid_t *id, struct stat *stbuf)
{
        return _errno(stor_getattr(pool, id, stbuf));
}

int block_listpool_open(const fileid_t *fileid, const char *uuid)
{
        return _errno(md_listpool_open(fileid, uuid));
}

int block_listpool(const fileid_t *fileid, const char *uuid, uint64_t offset, void *_de, int *_delen)
{
        return _errno(md_listpool(fileid, uuid, offset, _de, _delen));
}

int block_listpool_close(const fileid_t *fileid, const char *uuid)
{
        return _errno(md_listpool_close(fileid, uuid));
}

int block_lookup(const char *pool, const fileid_t *parent, const char *name, fileid_t *fileid)
{
        return _errno(stor_lookup(pool, parent, name, fileid));
}

int block_preload(const fileid_t *oid, int flag)
{
        (void) oid;
        (void) flag;

        UNIMPLEMENTED(__WARN__);

        return 0;
}

int block_connection(const chkid_t *chkid, void *list, int *count)
{
        (void) chkid;
        (void) list;
        (void) count;

        UNIMPLEMENTED(__WARN__);

        return 0;
}

int block_check_ready(const fileid_t *oid) //volume  is ready
{
        return _errno(md_check_ready(oid));
}

int block_reload(const fileid_t *oid)
{
        (void)oid;
        UNIMPLEMENTED(__WARN__);

        return 0;
}

int block_mkpool(const fileid_t *parent, const char *name, const setattr_t *_setattr, fileid_t *_fid)
{
        return _errno(stor_mkpool(parent, name, _setattr, _fid));
}


//TODO lich.tgt
int block_rmdir(const char *pool, const fileid_t *parent, const char *name)
{
        return _errno(stor_rmpool(pool, parent, name));
}

//TODO lich.tgt
int block_create(const char *pool, const fileid_t *parent, const char *name, const setattr_t *_setattr, fileid_t *fid,
                  uint64_t size)
{
        int ret;

        ret = stor_mkvol(parent, name, _setattr, fid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = stor_truncate(pool, fid, size);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return _errno(ret);
}

int block_unlink(const fileid_t *parent, const char *name)
{
        return _errno(stor_rmvol(parent, name, 0));
}

//TODO lich.tgt
int block_lookup1(const char *pool, const char *path, fileid_t *id)
{
        int ret;
        char parent[MAX_NAME_LEN], name[MAX_NAME_LEN];
        fileid_t parentid;

        if (strcmp(path, "/") == 0) {
                ret = block_root(pool, id);
                if (unlikely(ret))
                        GOTO(err_ret, ret);
        } else {
                ret = _path_split2(path, parent, name);
                if (unlikely(ret))
                        GOTO(err_ret, ret);

                ret = block_lookup1(pool, parent, &parentid);
                if (unlikely(ret)) {
                        GOTO(err_ret, ret);
                }

                ret = stor_lookup(pool, &parentid, name, id);
                if (unlikely(ret)) {
                        GOTO(err_ret, ret);
                }
        }

        return 0;
err_ret:
        return _errno(ret);
}

STATIC int __connection_add(const nid_t *nid, const volid_t *volid, const char *addr, int port, const char *context)
{
        int ret;
        nid_t local;

        local = *net_getnid();

        DINFO("vol "CHKID_FORMAT" peer %s/%d/%s:%d => ctl %s/%d context %s\n",
              CHKID_ARG(volid), network_rname(&local), local.id, addr, port,
              network_rname(nid), nid->id,
              context);

        if (net_islocal(nid)) {
                ret = stor_ctl_connect(volid, nid, addr);
                if (unlikely(ret))
                        GOTO(err_ret, ret);
        } else {
                ret = stor_rpc_connect(nid, volid, &local, addr);
                if (unlikely(ret))
                        GOTO(err_ret, ret);
        }

        return 0;
err_ret:
        return ret;
}

int block_connect(const char *pool, const volid_t *volid, const char *_addr, int port, const char *context)
{
        int ret;
        char _chkinfo[CHKINFO_MAX];
        chkinfo_t *chkinfo;

        chkinfo = (void *)_chkinfo;
        ret = md_chunk_getinfo(pool, NULL, volid, chkinfo, NULL);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = __connection_add(&chkinfo->diskid[0].id, volid, _addr, port, context);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

int block_disconnect(const char *pool, const volid_t *volid, const char *_addr, int port, const char *context)
{
        int ret;
        char _chkinfo[CHKINFO_MAX];
        chkinfo_t *chkinfo;
        nid_t *master = NULL;
        nid_t local;

        chkinfo = (void *)_chkinfo;
        ret = md_chunk_getinfo(pool, NULL, volid, chkinfo, NULL);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        master = &chkinfo->diskid[0].id;
        local = *net_getnid();

        DINFO("vol "CHKID_FORMAT" peer %s/%d/%s:%d => ctl %s/%d context %s\n",
              CHKID_ARG(volid), network_rname(&local), local.id, _addr, port,
              network_rname(master), master->id,
              context);

        if (net_islocal(master)) {
                ret = stor_ctl_disconnect(volid, master, _addr);
                if (unlikely(ret))
                        GOTO(err_ret, ret);
        } else {
                ret = stor_rpc_disconnect(master, volid, &local, _addr);
                if (unlikely(ret))
                        GOTO(err_ret, ret);
        }

        return 0;
err_ret:
        return ret;
}
